在Activity启动流程分析一篇中,我们介绍了从Launcher启动桌面应用的大体流程,本篇将是这一篇的补充,主要分析在Activity准备启动到显示过程中的详细内容,探讨关于WindowManager,Activity以及Window之间的联系。从而更进一步了解Android GUI 子系统。
应用进程创建
Activity启动是在ActivityThread中进行的,通过Zygote创建完应用进程后,首先会通过ActivityThread的main方法初始化UI线程,然后会通知ActivityManager进行attachApplication,这里AMS(ActivityManagerService)会看栈顶是否有等待启动的Activity,如果有的话就通过IApplicationThread通知应用Launch这个Activity。关于这个IApplication它实际是一个Binder,它负责AMS和应用进程之间的通信,在ActivityThread中的一系列schedulexxx方法都是通过这个Binder对象通知的。它的服务端是在应用进程一端,客户端是在AMS所在进程即SystemServer进程中。这个Binder是随着ActivityThread的创建而生成的,并在AMS进行attachApplication时将其传递给AMS。
1 | frameworks/base/core/java/android/app/ActivityThread.java |
1 | frameworks/base/core/java/android/app/ActivityThread.java |
AMS通过IApplicationThread的scheduleLaunchActivityl来启动一个Activity,启动过程会对应的创建一个ActivityClientRecord实例,这个和AMS中的ActivityRecord是对应的。这里最为重要的是token参数,它也是一个Binder对象,这个token实际上就是ActivityRecord中的appToken,在ActivityRecord构造时创建,它被AMS用来标记ActivityRecord对应的Activity。在AMS启动过程中会通过WMS调用addAppToken为该Activity添加一个AppWindowToken,appToken作为一个标识被传递给WMS。这样无论是应用进程还是WMS都可以通过这个Token来识别是否为同一个对象。
注:在Android Framework中一个Binder Token通常被用于进行binder通信或者唯一标记一个对象。
1 | frameworks/base/core/java/android/app/ActivityThread.java |
scheduleLaunchActivity 通过H,实际上是个Handler通知调用handleLaunchActivity启动Activity,这个方法首先调用performLaunchActivity创建Activity,然后通过handleResumeActivity显示activity。这两个方法做了Activity启动过程中的绝大部分事情。后面的内容主要据此展开。
Activity的创建
1 | frameworks/base/core/java/android/app/ActivityThread.java |
在performLaunchActivity中主要做以下事情:
- 根据r中的Activity信息创建Activity实例,这个是Instrumention通过反射来完成的。
- 为应用创建Application,当然这个Application只会创建一次,如果已经创建了就直接返回
- 为Activity创建Context,这里具体为ContextImpl
- 将activity attach到上下文Context
- 调用onCreate onStart等生命周期回调
接下来我们分步介绍以上几个步骤:
创建Activity实例
创建Activity实例的过程比较简单,因为Activity没有提供任何构造方法,这里通过反射默认构造一个实例。
创建Application
1 | //为应用创建application |
makeApplication主要为应用创建Application实例,在创建之前会先通过createAppContext创建AppcliationContext,然后调用onCreate回调。
创建contextImpl
1 | private Context createBaseContextForActivity(ActivityClientRecord r, |
每个Acitivty启动都需要为其创建一个Context,这里通过ContextImpl.createActivityContext创建
Activity的attach过程
1 | frameworks/base/core/java/android/app/Activity.java |
为Activity创建好context后接下来需要调用Activity的attach方法做以下事情:
- 设置context,将其保存在ContextWrapper中
- 保存token,这个token为ActivityRecord中的appToken
- 为Activity创建Window,即PhoneWindow
- 为Window设置WindowManager,这个WindowManager实际为WindowManagerImpl。
1 | frameworks/base/core/java/android/app/ContextImpl.java |
这里需要注意的是contextImpl在getSystemServer(Context.WINDOW_SERVICE)时会创建一个新的实例WindowManagerImpl,所以对于每个Window,都会有一个新的这样的实例。这个WindowManagerImpl并不是WMS的本地代理,这里不要混淆。接下来我们看看在setWindowManager中会做些什么事情
1 | public void setWindowManager(WindowManager wm, IBinder appToken, String appName, |
1 | public final class WindowManagerImpl implements WindowManager { |
setWindowManager为该window保存了Activity的appToken,同时通过createLocalWindowManager又创建了一个WindowManagerImpl。这次创建的实例中mParentWindow不为null,而是当前window,需要注意的是它内部持有一个
WindowManagerGlobal单例,这个WindowManagerGlobal是用来和WMS通信的,它内部就持有WMS的Binder本地代理接口对象。
生命周期的回调
Activity完成attach后就已经有对应的PhoneWindow对象了,同时将context保存在activity中了,这时候先回调onCreate,在onCreate中我们通过setContentView设置内容视图,其实是通过PhoneWindow对象的setContentView来完成的。
1 | frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java |
setContentView为当前window生成一个DecorView,它是整个view树的根view节点。
Activity的显示
通过上面的一系列工作,Activity已经准备好显示前的工作了,接下来就是开始handleResumeActivity了。
1 | final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, |
在这个方法中我们主要看activity是如何被显示出来的。首先获取已经inflate好的DecorView,将其设置为不可见,随后从Activity取到WindowManager(WindowManager是ViewManager的父类,它们都是接口),它实际就是之前通过createLocalWindowManager创建的WindowManagerImpl.随后通过wm.addView添加decorView到window中,这里最终会将window添加到WMS中去。前面我们知道和WMS的通信是由WindowManagerImpl内部的WindowManagerGlobal完成的,它是一个单例。
1 |
|
这里的mParentWindow就是activity对应的window,mDisplay代表显示终端对象。params为view的布局参数。
1 |
|
在addView中为当前decorview创建ViewRootImpl,这个是用来管理view树的,对应的将view,root,params分别添加到三个对应的数组中,最后通过ViewRootImpl的setView将当前view和ViewRootImpl关联起来。
1 | public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {//关联子视图 |
ViewRootImpl作为view树的管理者负责view树的绘制和输入事件的管理。其中最重要的工作是通过mWindowSession将当前window添加到WMS中去。这里需要注意mWindow是一个W对象,它是一个Binder对象,WMS使用它和应用进程通信,当窗口状态发生变化需要通过这个Binder通知应用端。
1 | //获取windowsession进行会话 也是一个单例 可见一个应用也只会有一个session |
mWindowSession是在创建ViewRootImpl通过WindowManagerGlobal的getWindowSession得到的。它返回一个IWindowSession是一个匿名binder,用来和WMS进行会话,在WindowManagerGlobal中它是这样打开一个会话的,它同样是一个单例,即一个应用进程只需要一个会话就可以了。
1 | frameworks/base/services/java/com/android/server/wm/Session.java |
得到该会话就可以通过addToDisplay将window添加到WMS了,它实际是和Session交互的,Session是IWindowSession匿名binder的服务端,mService是WMS服务。
1 | //添加window到WMS |
首先我们注意到两个map
1 | final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>(); |
mWindowMap是IWindow和服务端Window的映射,这里WindowState即代表了服务端的Window,而IWindow是ViewRootImpl中的W对象。
mTokenMap是token和WindowToken的映射,这里的token为appToken,windowToken为我们在启动activity之前通过addApptoken添加的。
在addWindow中主要完成以下事情:
- 通过token取到WindowToken,这个token来自 WindowManager.LayoutParams,它是在WindowManagerGlobal的addView中通过adjustLayoutParamsForSubWindows设置的,它会根据不同的窗口类型和属性设置该token.
- 如果未取到WindowToken需要为其创建一个。
- 随后为该窗口创建WindowState,它代表了应用端的Window。同时将其添加到mWindowMap中
- 根据不同的窗口类型设置窗口的Z序。
关于添加窗口的具体细节,这里就不详述,我会在后面的文章后介绍。
显示的时机
在handleResumeActivity通过addView添加view,将window加入到WMS后,会通过updateViewLayout更新视图
1 | frameworks/base/core/java/android/view/WindowManagerGlobal.java |
在root.setLayoutParams中会通过scheduleTraversals()绘制view。